﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyModel;

namespace Jack.EntityFrameworkCore
{
    public abstract class JackDbContext<TDbContext> : DbContext where TDbContext : DbContext
    {
        public JackDbContext()
        {

        }

        public JackDbContext(DbContextOptions<TDbContext> options)
            : base(options)
        {

        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // 全局关闭追踪
            //optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);

            base.OnConfiguring(optionsBuilder);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // 自动注册数据库模型，无需自己再上下文中写DbSet
            try
            {
                // 载入所有项目类库
                var compileLibraries = DependencyContext.Default.CompileLibraries
                .Where(x => !x.Serviceable && x.Type == "project");

                // 获取所有数据库模型
                foreach (var compileLibrary in compileLibraries)
                {
                    // 加载程序集中指定的数据库类型
                    var types = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(compileLibrary.Name)).GetExportedTypes()
                        .Where(type => type.GetTypeInfo().BaseType != null && !type.IsAbstract && !type.IsInterface && !type.IsGenericType && type.IsClass && IsAssignableTo<IBaseModel>(type));
                    foreach (var type in types)
                    {
                        modelBuilder.Entity(type);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw;
            }

            base.OnModelCreating(modelBuilder);
        }

        private static bool IsAssignableTo<T>(Type @this)
        {
            if (@this == null)
            {
                throw new ArgumentNullException("this");
            }

            return typeof(T).IsAssignableFrom(@this);
        }

        public async Task<IDbContextTransaction> BeginTransactionAsync()
        {
            IDbContextTransaction dbContextTransaction = Database.CurrentTransaction;
            dbContextTransaction ??= await Database.BeginTransactionAsync();
            return dbContextTransaction;
        }

        public IDbContextTransaction CurrentTransaction => Database.CurrentTransaction;
    }
}
